#include <QString>
#include <iostream>

#include "properties_reader.h"
#include "properties_item.h"
#include "properties_model.h"

CPropertiesModel::CPropertiesModel(QObject* pParent) :
	QAbstractItemModel(pParent),
	mLoaded(false),
	mReadOnly(false)
{
	QVector<QVariant> lRootData;
	lRootData << tr("Key") << tr("Value");

	mRootItem = new CPropertiesItem(lRootData);
}

CPropertiesModel::~CPropertiesModel()
{
	delete mRootItem;
}

QModelIndex CPropertiesModel::index(int pRow, int pColumn, const QModelIndex& pParent) const
{
	if (pParent.isValid() && pParent.column() != 0)
	{
		return QModelIndex();
	}
	CPropertiesItem* lParentItem = getItem(pParent);

	CPropertiesItem* lChildItem = lParentItem->child(pRow);
	if (lChildItem)
	{
		return createIndex(pRow, pColumn, lChildItem);
	}

	return QModelIndex();
}

QModelIndex CPropertiesModel::parent(const QModelIndex& pIndex) const
{
	if (!pIndex.isValid())
	{
		return QModelIndex();
	}

	CPropertiesItem* lChildItem = getItem(pIndex);
	CPropertiesItem* lParentItem = lChildItem->parent();

	if (lParentItem == mRootItem)
	{
		return QModelIndex();
	}

	return createIndex(lParentItem->childNumber(), 0, lParentItem);
}

int CPropertiesModel::rowCount(const QModelIndex& pParent = QModelIndex()) const
{
	CPropertiesItem* lItem = getItem(pParent);

	return lItem->childCount();
}

int CPropertiesModel::columnCount(const QModelIndex& pParent = QModelIndex()) const
{
	return mRootItem->columnCount();
}

QVariant CPropertiesModel::data(const QModelIndex& pIndex, int pRole) const
{
	if (!pIndex.isValid())
	{
		return QVariant();
	}

	if (pRole != Qt::DisplayRole && pRole != Qt::EditRole)
	{
		return QVariant();
	}

	CPropertiesItem* lItem = getItem(pIndex);

	return lItem->data(pIndex.column());
}

bool CPropertiesModel::setData(const QModelIndex &pIndex, const QVariant &pValue, int pRole)
{
	if (pRole != Qt::EditRole)
	{
		return false;
	}

	CPropertiesItem *lItem = getItem(pIndex);
	bool lResult = lItem->setData(pIndex.column(), pValue);

	if (lResult)
	{
		emit dataChanged(pIndex, pIndex);
	}

	return lResult;
}

Qt::ItemFlags CPropertiesModel::flags(const QModelIndex& pIndex) const
{
	if (!pIndex.isValid())
	{
		return 0;
	}

	if (parentIsRoot(pIndex.parent()) && pIndex.column() == 1)
	{
		return 0;
	}

	if (mReadOnly)
	{
		return Qt::ItemIsSelectable;
	}

	return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

QVariant CPropertiesModel::headerData(int pSection, Qt::Orientation pOrientation, 
		int pRole) const
{
	if (pOrientation == Qt::Horizontal && pRole == Qt::DisplayRole)
	{
		return mRootItem->data(pSection);
	}

	return QVariant();
}

void CPropertiesModel::sort(int pColumn, Qt::SortOrder pOrder)
{
	beginResetModel();
	for (int lOuterCnt = 0; lOuterCnt < (mRootItem->childCount() - 1); lOuterCnt++)
	{
		for (int lInnerCnt = lOuterCnt + 1; lInnerCnt < mRootItem->childCount(); lInnerCnt++)
		{
			if (((mRootItem->child(lOuterCnt)->data(0).toString() >
					mRootItem->child(lInnerCnt)->data(0).toString()) &&
					pOrder == Qt::AscendingOrder) ||
					((mRootItem->child(lOuterCnt)->data(0).toString() <
					mRootItem->child(lInnerCnt)->data(0).toString()) &&
					pOrder == Qt::DescendingOrder))

			{
				CPropertiesItem* lTempItem = mRootItem->child(lOuterCnt);
				mRootItem->setChild(lOuterCnt, mRootItem->child(lInnerCnt));
				mRootItem->setChild(lInnerCnt, lTempItem);
			}
		}
	}
	endResetModel();
	emit modelSorted();
}

void CPropertiesModel::configReceived(const QString& pSection, const QString& pKey, const QString& pValue)
{
	QModelIndex lSectionIndex = insertSection(pSection);
	insertKey(lSectionIndex, pKey, pValue);
}

bool CPropertiesModel::loadProperties(const QString& pFilename)
{
	dropProperties();
	CPropertiesReader lPropertiesReader(pFilename.toStdString());
	if (!lPropertiesReader.loadIniFile())
	{
		// something went terribly wrong ...
		return false;
	}

  TSectionIterator lIteratorSections;
  TSectionIterator lIteratorSectionsEnd;
  TKeyIterator lIteratorKeys;
  TKeyIterator lIteratorKeysEnd;

  if (!(lPropertiesReader.getSectionIteratorBegin(lIteratorSections) &&
          lPropertiesReader.getSectionIteratorEnd(lIteratorSectionsEnd)))
  {
    return false;
  }

  for (;lIteratorSections != lIteratorSectionsEnd; lIteratorSections++)
  {
    if (!(lPropertiesReader.getKeyIteratorBegin(lIteratorSections->first, lIteratorKeys) &&
          lPropertiesReader.getKeyIteratorEnd(lIteratorSections->first, lIteratorKeysEnd)))
    {   
      return false;
    }   
    QModelIndex lIndex = insertSection(QString((lIteratorSections->first).c_str()));
    for (;lIteratorKeys != lIteratorKeysEnd; lIteratorKeys++)
    {   
			insertKey(lIndex, QString(lIteratorKeys->first.c_str()), QString(lIteratorKeys->second.c_str()));
    }   
  }

	return true;
}

bool CPropertiesModel::saveProperties(const QString& pFilename)
{
	CPropertiesReader lPropertiesReader(pFilename.toStdString(), OPEN_NOAUTOLOAD);

	for(int lCntSection = 0; lCntSection < mRootItem->childCount(); lCntSection++)
	{
		CPropertiesItem* lChild = mRootItem->child(lCntSection);
		lPropertiesReader.createSection(lChild->data(0).toString().toStdString());
		for(int lCntKey = 0; lCntKey < lChild->childCount(); lCntKey++)
		{
			CPropertiesItem* lKey = lChild->child(lCntKey);
			lPropertiesReader.createKey(lChild->data(0).toString().toStdString(),
					lKey->data(0).toString().toStdString(),
					lKey->data(1).toString().toStdString());
		}
	}

	return lPropertiesReader.write();
}

void CPropertiesModel::sendConfig(CClientConnection* pConnection)
{
	pConnection->sendConfigStart();
	for(int lCntSection = 0; lCntSection < mRootItem->childCount(); lCntSection++)
	{
		CPropertiesItem* lChild = mRootItem->child(lCntSection);
		pConnection->sendConfigSection(lChild->data(0).toString());
		for(int lCntKey = 0; lCntKey < lChild->childCount(); lCntKey++)
		{
			CPropertiesItem* lKey = lChild->child(lCntKey);
			pConnection->sendConfigKey(lChild->data(0).toString(),
					lKey->data(0).toString(),
					lKey->data(1).toString());
		}
	}
	pConnection->sendConfigEnd();
}

bool CPropertiesModel::insertRows(int pPosition, int pRows, 
		const QModelIndex &pParent)
{
	CPropertiesItem *lParentItem = getItem(pParent);

	bool lSuccess;

	beginInsertRows(pParent, pPosition, pPosition + pRows - 1);
	lSuccess = lParentItem->insertChildren(pPosition, pRows, mRootItem->columnCount());
	endInsertRows();

	return lSuccess;
}

bool CPropertiesModel::removeRows(int pPosition, int pRows, 
		const QModelIndex &pParent)
{
	CPropertiesItem* lParentItem = getItem(pParent);
	bool lSuccess = true;

	beginRemoveRows(pParent, pPosition, pPosition + pRows - 1);
	lSuccess = lParentItem->removeChildren(pPosition, pRows);
	endRemoveRows();

	return lSuccess;
}

QModelIndex CPropertiesModel::insertSection(const QVariant& pSectionName)
{
	/* check if section already exists */
	for (int lCnt = 0; lCnt < mRootItem->childCount(); lCnt++)
	{
		if(mRootItem->child(lCnt)->data(0).toString() == 
				pSectionName.toString())
		{
			return createIndex(0, 0, mRootItem->child(lCnt));
		}
	}

	if (!insertRow(0, QModelIndex()))
	{
		 return QModelIndex();
	}

	QModelIndex lChild = index(0, 0, QModelIndex());
	setData(lChild, pSectionName, Qt::EditRole);
	return lChild;
}

QModelIndex CPropertiesModel::insertKey(const QModelIndex& pIndex, const QVariant& pKey, const QVariant& pValue)
{
	if(!insertRow(0, pIndex))
	{
		return QModelIndex();
	}
	QModelIndex lChild = index(0, 0, pIndex);
	setData(lChild, pKey, Qt::EditRole);
	lChild = index(0, 1, pIndex);
	setData(lChild, pValue, Qt::EditRole);
	return index(0, 0, pIndex);
}

bool CPropertiesModel::parentIsRoot(const QModelIndex& pIndex) const
{
	CPropertiesItem* lParentItem = getItem(pIndex);
	return (lParentItem == mRootItem);
}

CPropertiesItem* CPropertiesModel::getItem(const QModelIndex& pIndex) const
{
	if (pIndex.isValid())
	{
		CPropertiesItem* lItem = static_cast<CPropertiesItem*>(pIndex.internalPointer());
		if (lItem)
		{
			return lItem;
		}
	}
	return mRootItem;
}

void CPropertiesModel::dropProperties()
{
	if (mRootItem->childCount() > 0)
	{
		beginRemoveRows(QModelIndex(), 0, mRootItem->childCount() - 1);
		mRootItem->removeChildren(0,mRootItem->childCount());
		endRemoveRows();
	}
}

